import random
import numpy as np
# random.seed(8636)

#将全局的最大值记为MAX，全局的最小值记为MIN
#模拟一段数据需要参考前一段的数据，将前一段数据的最大值记为max，最小值记为min，均值记为mean，将mean值归一化为smean
#以smean值为基础，模拟的第一个值在smean的基础上加减一定的斜率，从模拟的第二个值开始，在前一个值的基础上加减一定的斜率
#最后我们得到一个列表，包含许多小数，根据max和min将其反归一化还原

def genSquare(N,base,dir='up'):
    '''
    生成num个数据，第一段上升比较急，第二段较为平缓，第三段下降较快
    :param N: 数据点数
    :param base: 最开始的基准值
    :param dir: 方向，非'up'则为'down'
    :return: list
    '''
    ls=[]
    k1=[0.05,0.2]#上升段的斜率
    k2=[-0.05,0.05]#中间段的斜率
    k3=[-0.2,-0.05]#下降段的斜率
    step=N//3#假定最好的情况均分三段
    frag1=[0,int(random.uniform(0,0.5*step))]#第一段的起止
    frag3 = [int(random.uniform(2.5*step,N)), N]  # 第三段的起止
    frag2=[frag1[1],frag3[0]]#第二段的起止
    lastNum=base
    if dir!='up':
        k1,k3=k3,k1
    gaussNoise=random.uniform((k1[1]-k1[0])/3,k1[1]-k1[0])
    for i in range(frag1[0],frag1[1]):
        num=lastNum+random.uniform(k1[0],k1[1])+random.gauss(0,gaussNoise)
        lastNum = num
        ls.append(num)
    for i in range(frag2[0],frag2[1]):
        num=lastNum+random.uniform(k2[0],k2[1])
        lastNum=num
        ls.append(num)
    gaussNoise = random.uniform((k3[1] - k3[0]) / 3, k3[1] - k3[0])
    for i in range(frag3[0],frag3[1]):
        num = lastNum + random.uniform(k3[0], k3[1])+random.gauss(0,gaussNoise)
        lastNum = num
        ls.append(num)
    return ls

def genSquare2(N,base,dir='up'):
    '''
    生成num个数据，数据分布粗略像方波，第一段是上升的凸弧，第二段平缓,第三段是下降的凸弧
    :param N: 数据点数
    :param base: 最开始的基准值
    :param dir: 方向，非'up'则为'down'
    :return: list
    '''
    ls = []
    k1 = [-0.05, 0.2]  # 上升段的斜率
    k2=[-0.05,0.05]
    k3 = [-0.2, 0.05]  # 下降段的斜率
    step = N // 5  # 假定最好的情况均分五段
    frag1 = [0, int(random.uniform(0, step))]  # 第一段的起止
    frag3 = [int(random.uniform(4*step,5*step)), N]  # 第三段的起止
    frag2= [frag1[1],frag3[0]] # 第二段的起止
    lastNum = base
    if dir!='up':
        k1, k3 = k3, k1
    for i in range(frag1[0], frag1[1]):
        num = lastNum + random.uniform(k1[0], k1[1])
        lastNum = num
        ls.append(num)
    for i in range(frag2[0], frag2[1]):
        num = lastNum + random.uniform(k2[0], k2[1])
        lastNum = num
        ls.append(num)
    for i in range(frag3[0], frag3[1]):
        num = lastNum + random.uniform(k3[0],k3[1])
        lastNum = num
        ls.append(num)
    return ls


def genSquare3(N, base):
    '''
    生成num个数据，数据是严格的方波，第一段是上升的凸弧，第二段平缓,第三段是下降的凸弧
    :param N: 数据点数
    :param base: 最开始的基准值
    :param dir: 方向，非'up'则为'down'
    :return: list
    '''
    ls = []
    k = random.uniform(-0.2, 0.2)  # 上升段的斜率
    step = N // 3  # 假定最好的情况均分五段
    frag1 = [0, int(random.uniform(0, step))]  # 第一段的起止
    frag3 = [int(random.uniform(2 * step, 3 * step)), N]  # 第三段的起止
    frag2 = [frag1[1], frag3[0]]  # 第二段的起止
    for i in range(frag1[0], frag1[1]):
        ls.append(base)
    num=base+k
    for i in range(frag2[0], frag2[1]):
        ls.append(num)
    for i in range(frag3[0], frag3[1]):
        ls.append(base)
    return ls

def genArc(N,base,dir='up'):
    '''
    生成num个数据，数据分布类似拱函数
    :param N: 数据点数
    :param base: 最开始的基准值
    :param dir: 方向，非'up'则为'down'
    :return: list
    '''
    ls = []
    kran = [-0.1, 0.1]  # 斜率范围
    lastNum = base
    if dir=='down':
        k=kran[0]
        kstep = (kran[1] - kran[0]) / N
        for i in range(0, N):
            num = lastNum + random.gauss(k,k)#gauss函数的μ表示均值，σ表示方差
            k+=kstep
            lastNum = num
            ls.append(num)
    else:
        kran=[kran[1],kran[0]]
        k=kran[0]
        kstep = (kran[1] - kran[0]) / N
        for i in range(0, N):
            num = lastNum + random.gauss(k,k)
            k+=kstep
            lastNum = num
            ls.append(num)
    return ls

def genLinear(N,base,dir='up'):
    ls = []
    kran = [0, 0.1]  # 斜率范围
    lastNum = base
    if dir == 'up':
        for i in range(0, N):
            k = random.uniform(kran[0], kran[1])
            num = lastNum + random.gauss(k, 2*k)
            lastNum = num
            ls.append(num)
    else:
        kran = [-kran[1], kran[0]]
        for i in range(0, N):
            k = random.uniform(kran[0], kran[1])
            num = lastNum + random.gauss(k, 2 * k)
            lastNum = num
            ls.append(num)
    return ls

def genGauss(N,base,miu=0,sigma=0.1):
    ls = []
    lastNum = base
    for i in range(0, N):
        num = lastNum + random.gauss(miu,sigma)
        lastNum = num
        ls.append(num)
    return ls

def genFlat(N,base):
    ls = []
    lastNum = base
    for i in range(0, N):
        num = lastNum + random.gauss(0,0.01)
        lastNum = num
        ls.append(num)
    return ls

def gen(SMIN,SMAX,base,MIN,MAX,N,M):
    """
    生成M段数据，每段数据长度为N。生成的数据限定在[MIN,MAX]之间。
    假定了参照前一段数据（实际可以不存在前一段数据）,
    [SMIN,SMAX]是这段数据参照[MIN,MAX]范围的占比，
    base是这段数据的最后一个值，
    最后返回的是一整个一维numpy数组
    """
    max=(MAX-MIN)*SMAX+MIN
    min=(MAX-MIN)*SMIN+MIN
    sbase = (base - MIN) / (MAX - MIN)
    counter=0
    simData=np.array([])
    while True:
        rand = np.random.uniform(0, 1)
        if rand < sbase or sbase > 0.9:
            dir = 'down'
        elif rand >= sbase or sbase<0.1:
            dir = 'up'
        rand = np.random.uniform(0, 1)
        if sbase > 0.9 or sbase < 0.1 or SMAX-SMIN<0.05:
            if rand < 0.4:
                frag = genLinear(N, base, dir)
            elif rand < 0.6:
                frag = genSquare(N, base, dir)
            elif rand < 0.8:
                frag = genSquare2(N, base, dir)
            elif rand < 1:
                frag = genArc(N, base, dir)
        else:
            if rand < 0.3:
                frag = genFlat(N, base)
            elif rand < 0.5:
                frag = genGauss(N, base)
            elif rand < 0.7:
                frag = genLinear(N, base, dir)
            elif rand < 0.8:
                frag = genSquare(N, base, dir)
            elif rand < 0.9:
                frag = genSquare2(N, base, dir)
            elif rand < 1:
                frag = genArc(N, base, dir)
        frag = np.array(frag) * (max - min) + min
        if frag.max() > MAX or frag.min() < MIN or np.isnan(frag).all() or frag.max()==frag.min():
            continue
        simData = np.append(simData, frag)
        preFrag = simData[-N:]
        min = preFrag[~np.isnan(preFrag)].min()
        max = preFrag[~np.isnan(preFrag)].max()
        sbase = (preFrag[-1] - MIN) / (MAX - MIN)
        SMIN = (min - MIN) / (MAX - MIN)
        SMAX = (max - MIN) / (MAX - MIN)
        preFrag = (preFrag - min) / (max - min)
        base = preFrag[-1]
        counter+=1
        if counter==M:
            break
    return simData

def gen2(VRan,LRan,M):
    """
    生成M个数据这M个数据是由好多段随机生成的数据组成的每段数据长度限定在LRan。
    生成的数据值大小限定在VRan之间。
    最后返回的是一整个一维numpy数组。
    """
    MIN,MAX=VRan
    SMIN=np.random.uniform(0,0.5)
    SMAX=np.random.uniform(SMIN,1)
    base=np.random.uniform(SMIN,SMAX)
    sbase = (base - MIN) / (MAX - MIN)
    max = (MAX - MIN) * SMAX + MIN
    min = (MAX - MIN) * SMIN + MIN
    simData=np.array([])
    while simData.shape[0]<M:
        rand = np.random.uniform(0, 1)
        if rand < sbase or sbase > 0.9:
            dir = 'down'
        elif rand >= sbase or sbase < 0.1:
            dir = 'up'
        rand = np.random.uniform(0, 1)
        N=np.random.randint(*LRan)
        if M-simData.shape[0]<=LRan[1]:
            N=M-simData.shape[0]
        if sbase > 0.9 or sbase < 0.1 or SMAX - SMIN < 0.05:
            if rand < 0.4:
                frag = genLinear(N, base, dir)
            elif rand < 0.6:
                frag = genSquare(N, base, dir)
            elif rand < 0.8:
                frag = genSquare2(N, base, dir)
            elif rand < 1:
                frag = genArc(N, base, dir)
        else:
            if rand < 0.3:
                frag = genFlat(N, base)
            elif rand < 0.5:
                frag = genGauss(N, base)
            elif rand < 0.7:
                frag = genLinear(N, base, dir)
            elif rand < 0.8:
                frag = genSquare(N, base, dir)
            elif rand < 0.9:
                frag = genSquare2(N, base, dir)
            elif rand < 1:
                frag = genArc(N, base, dir)
        frag = np.array(frag) * (max - min) + min
        if frag.max() > MAX or frag.min() < MIN or frag.max()==frag.min():
            continue
        simData = np.append(simData, frag)
        preFrag = simData[-N:]
        min = np.nanmin(preFrag)
        max = np.nanmax(preFrag)
        sbase = (preFrag[-1] - MIN) / (MAX - MIN)
        SMIN = (min - MIN) / (MAX - MIN)
        SMAX = (max - MIN) / (MAX - MIN)
        preFrag = (preFrag - min) / (max - min)
        base = preFrag[-1]
    return simData

if __name__=="__main__":
    import matplotlib.pyplot as plt
    dir='down'
    num=1000
    base=0.4
    SMIN = np.random.uniform(0, 0.1)
    SMAX = np.random.uniform(0.8, 1)
    base = np.random.uniform(SMIN, SMAX)
    nums=gen(SMIN,SMAX,base,20,136,40,100)
    nums= gen2([20,136],[30,60],3899)
    x=[i for i in range(1,len(nums)+1)]
    plt.plot(x,nums)
    plt.show()
